<template>
  <div class="page-wrap">
    <div class="page-l">
      <div
        :class="{
          'video-main': showType === '1',
          'video-sub': showType === '0'
        }"
        @click.stop="handleChangeShowType">
        <i v-if="!remoteVideoFlag" class="iconfont icon-16shipin-4"></i>
        <i class="iconfont icon-maikefeng" :class="{ active: remoteAudioFlag }"></i>
        <video
          ref="videoRemote"
          autoplay>
        </video>
      </div>

      <div
        :class="{
          'video-main': showType === '0',
          'video-sub': showType === '1'
          }"
        @click.stop="handleChangeShowType">
        <i v-if="!localVideoFlag" class="iconfont icon-16shipin-4"></i>
        <i class="iconfont icon-maikefeng" :class="{ active: localAudioFlag }"></i>
        <video
          ref="videoLocal"
          autoplay>
        </video>
      </div>

      <div class="question-wrap">
        <ul class="tab-list">
          <li
            v-for="(item, index) in questionList"
            class="tab-item"
            :key="index"
            :class="{ 'tab-item-active': index === curQuestion }"
            @click="handleChangeQuestion(index)">
            题目{{index + 1}}
          </li>

          <li
            v-if="questionList.length < 5 && type === '0'"
            class="tab-item tab-item-add"
            @click="handleAddQuestion">
            新增
          </li>
        </ul>

        <div
          v-if="type === '1'"
          class="question-content">
          <div
            class="text"
            :class="{'no-data': questionList[curQuestion].content === '' }">
            {{questionList[curQuestion].content}}
          </div>
        </div>

        <textarea
          v-else
          v-model="questionList[curQuestion].content"
          class="question-input"
          @input="handleEditQuestion">
        </textarea>
      </div>
    </div>

    <div class="page-r">
      <div class="editor-wrap">
        <div class="editor-top-bar">
          选择语言
          <select
            v-model="curLanguage"
            @change="handleChangeLanguage">
            <option
              v-for="language in languageList"
              :key="language"
              :value="language">
              {{language}}
            </option>
          </select>
        </div>

        <div ref="editor" class="editor-wrap-inner"></div>

        <div class="editor-bottom-bar">
          <p class="tips">温馨提示：代码中打印的内容，将在执行后显示在下方</p>
          <button class="btn" @click="handleRun">执行代码</button>
        </div>
      </div>

      <div
        class="page-r-b"
        :class="{ 'no-result': codeResult === '' }">
        {{codeResult}}
      </div>
    </div>
  </div>
</template>

<script>
import socket from '@/utils/socket'
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js'

let localstream = null
let monacoModel = null
let monacoInstance = null

export default {
  data () {
    return {
      roomId: '',
      type: '', // 0-面试官、1-面试者
      showType: '1', // 0-本地、1-远程

      questionList: [
        { content: '', code: '' }
      ],
      curQuestion: 0,

      languageList: ['javascript', 'css', 'html'],
      curLanguage: 'javascript',

      localVideoFlag: false,
      localAudioFlag: false,
      remoteVideoFlag: false,
      remoteAudioFlag: false,
      peerConn: null,

      codeResult: ''
    }
  },
  methods: {
    initSocket () {
      socket.on('joined', (data) => {
        // todo...
      })

      socket.on('candidate', (data) => {
        // 接收到 candidate
        this.addCandidate(data)
      })

      socket.on('offer', (data) => {
        // 接收到 offer
        this.addOffer(data)
      })

      socket.on('answer', (data) => {
        // 接收到 answer
        this.addAnswer(data)
      })

      socket.on('showQuestion', (data) => {
        // 接收到 question
        if (this.questionList[data.questionIndex]) {
          this.questionList[data.questionIndex].content = data.question.content
        } else {
          this.questionList.splice(data.questionIndex, 0, {
            content: data.question.content,
            code: ''
          })
        }
      })

      if (this.type === '0') {
        socket.on('code', (data) => {
          // 接收到 code
          this.questionList[data.index].code = data.code
          if (data.index === this.curQuestion) {
            monacoInstance.setValue(this.questionList[data.index].code)
          }
        })
      }

      this.join()
    },
    join () {
      socket.emit('join', {
        roomId: this.roomId,
        type: this.type
      })
    },
    async createMedia () {
      try {
        const constrains = {
          audio: false,
          video: false
        }

        if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
          throw new Error('无法获取本地媒体信息')
        }

        const mediaDevices = await navigator.mediaDevices.enumerateDevices()

        mediaDevices.forEach(item => {
          if (item.kind === 'audioinput') {
            constrains.audio = true
          } else if (item.kind === 'videoinput') {
            constrains.video = true
          }
        })

        if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
          // 最新标准API
          localstream = await navigator.mediaDevices.getUserMedia(constrains)
        } else if (navigator.webkitGetUserMedia) {
          // webkit内核浏览器
          localstream = await navigator.webkitGetUserMedia(constrains)
        } else if (navigator.mozGetUserMedia) {
          // Firefox浏览器
          localstream = await navigator.mozGetUserMedia(constrains)
        } else if (navigator.getUserMedia) {
          // 旧版API
          localstream = await navigator.getUserMedia(constrains)
        } else {
          throw new Error('无法获取本地媒体信息')
        }

        this.$refs.videoLocal.srcObject = localstream
        this.localVideoFlag = constrains.video
        this.localAudioFlag = constrains.audio
      } catch (e) {
        this.localVideoFlag = false
        this.localAudioFlag = false
        console.log('Err in createMedia: ', e)
      }

      this.createPeerConn()
    },
    createPeerConn () {
      try {
        const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection

        this.peerConn = new PeerConnection()
        this.peerConn.addStream(localstream)

        // 监听ICE候选信息，如果收集到，就发送给对方
        this.peerConn.onicecandidate = (event) => {
          if (event.candidate) {
            socket.emit('candidate', {
              roomId: this.roomId,
              type: this.type,
              candidate: event.candidate
            })
          }
        }

        this.peerConn.onaddstream = (event) => {
          this.$refs.videoRemote.srcObject = event.stream
          const arrA = event.stream.getVideoTracks()
          const arrB = event.stream.getAudioTracks()
          this.remoteVideoFlag = arrA.length > 0
          this.remoteAudioFlag = arrB.length > 0
        }
      } catch (e) {
        console.log('Err in createPeerConn: ', e)
      }

      this.createOffer()
    },
    async createOffer () {
      try {
        const offer = await this.peerConn.createOffer({
          offerToReceiveAudio: 1,
          offerToReceiveVideo: 1
        })

        await this.peerConn.setLocalDescription(offer)

        socket.emit('offer', {
          roomId: this.roomId,
          type: this.type,
          offer: offer
        })
      } catch (e) {
        console.log('Err in createOffer: ', e)
      }
    },
    async addCandidate (data) {
      try {
        // 设置远程 candidate
        await this.peerConn.addIceCandidate(data.candidate)
      } catch (e) {
        console.log('Err in addCandidate: ', e)
      }
    },
    async addOffer (data) {
      try {
        // 接收端设置远程 offer 描述
        await this.peerConn.setRemoteDescription(data.offer)
        // 接收端创建 answer
        const answer = await this.peerConn.createAnswer()
        // 接收端设置本地 answer 描述
        await this.peerConn.setLocalDescription(answer)
        // 给对方发送 answer
        socket.emit('answer', {
          roomId: this.roomId,
          type: this.type,
          answer: answer
        })
      } catch (e) {
        console.log('Err in addOffer: ', e)
      }
    },
    async addAnswer (data) {
      try {
        await this.peerConn.setRemoteDescription(data.answer)
      } catch (e) {
        console.log('Err in addAnswer: ', e)
      }
    },
    handleAddQuestion () {
      this.questionList.push({
        content: '',
        code: ''
      })
      this.curQuestion = this.questionList.length - 1
    },
    handleChangeShowType () {
      this.showType = this.showType === '0' ? '1' : '0'
    },
    handleChangeLanguage () {
      monaco.editor.setModelLanguage(monacoModel, this.curLanguage)
    },
    handleChangeQuestion (index) {
      this.curQuestion = index
      monacoInstance.setValue(this.questionList[index].code)
    },
    handleEditQuestion () {
      socket.emit('showQuestion', {
        roomId: this.roomId,
        type: this.type,
        question: {
          content: this.questionList[this.curQuestion].content
        },
        questionIndex: this.curQuestion
      })
    },
    handleRun () {
      const _this = this
      const myConsole = {
        log () {
          _this.codeResult += Array.from(arguments).map(item => {
            return item.toString()
          }).join(' ')
          _this.codeResult += '\r\n'
        },
        info () {
          _this.codeResult += Array.from(arguments).map(item => {
            return item.toString()
          }).join(' ')
          _this.codeResult += '\r\n'
        }
      }
      const run = (code, console) => {
        eval(code)
      }

      _this.codeResult = ''
      run(monacoInstance.getValue(), myConsole)
    }
  },
  created () {
    if (this.$route.params.roomId && this.$route.params.type) {
      this.roomId = this.$route.params.roomId
      this.type = this.$route.params.type
      this.initSocket()
    }

    window.vm = this
  },
  mounted () {
    this.createMedia()

    monacoModel = monaco.editor.createModel('', this.curLanguage)
    monacoInstance = monaco.editor.create(this.$refs.editor, {
      model: monacoModel,
      readOnly: this.type === '0',
      tabSize: 2
    })

    monacoModel.onDidChangeContent((e) => {
      const code = monacoInstance.getValue()

      this.questionList[this.curQuestion].code = code

      if (this.type === '1') {
        socket.emit('code', {
          roomId: this.roomId,
          type: this.type,
          code: code,
          index: this.curQuestion
        })
      }
    })

    // 为了便于调试，将两个代码编辑器实例挂到window上
    window.mi = monacoInstance
    window.mm = monacoModel

    window.onresize = () => {
      monacoInstance.layout()
    }
  },
  beforeDestroy () {
    monacoInstance.dispose()
    monacoModel.dispose()
    window.onresize = null
  }
}
</script>

<style lang="less" scoped>
.page-wrap{
  height: 100%;
  position: relative;
  padding-left: 380px;
  background-color: #333333;
  .page-l{
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 380px;
    padding: 290px 10px 10px 10px;
    .video-tab{
      position: absolute;
      top: 0;
      left: 20px;
      right: 20px;
      height: 20px;
      line-height: 20px;
      border-radius: 0 0 6px 6px;
      background-color: #333333;
    }
    .video-main{
      position: absolute;
      top: 10px;
      left: 10px;
      width: 360px;
      height: 270px;
      border-radius: 4px;
      overflow: hidden;
      background-color: #efefef;
      cursor: pointer;
      .icon-maikefeng{
        position: absolute;
        top: 0;
        left: 0;
        width: 21px;
        height: 20px;
        line-height: 20px;
        text-align: center;
        background-color: rgba(0, 0, 0, 0.5);
        border-radius: 4px 0 4px 0;
        color: #d7d7d7;
        &.active{
          color: #43ff52;
        }
      }
      .icon-16shipin-4{
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        text-align: center;
        color: #999999;
        font-size: 64px;
      }
      video{
        width: 100%;
        height: 100%;
      }
    }
    .video-sub{
      position: absolute;
      z-index: 1;
      top: 195px;
      left: 258px;
      width: 107px;
      height: 80px;
      border-radius: 4px;
      overflow: hidden;
      background-color: #c9c9c9;
      cursor: pointer;
      .icon-maikefeng{
        position: absolute;
        top: 0;
        left: 0;
        width: 21px;
        height: 20px;
        line-height: 20px;
        text-align: center;
        background-color: rgba(0, 0, 0, 0.5);
        border-radius: 4px 0 4px 0;
        color: #d7d7d7;
        &.active{
          color: #43ff52;
        }
      }
      .icon-16shipin-4{
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        text-align: center;
        color: #999999;
        font-size: 32px;
      }
      video{
        width: 100%;
        height: 100%;
      }
    }
    .question-wrap{
      height: 100%;
      position: relative;
      padding-top: 36px;
      .tab-list{
        position: absolute;
        top: 0;
        height: 30px;
        padding-bottom: 10px;
        overflow: hidden;
      }
      .tab-item{
        height: 26px;
        line-height: 26px;
        text-align: center;
        padding: 0 8px;
        font-size: 12px;
        border-radius: 4px;
        background-color: #9f9f9f;
        color: #ffffff;
        float: left;
        margin-right: 10px;
        cursor: pointer;
        &.tab-item-active{
          background-color: #427bf0;
        }
        &.tab-item-add{
          background-color: #8942f0;
        }
      }
      .question-content{
        height: 100%;
        overflow: auto;
        font-size: 0;
        border-radius: 4px;
        background-color: #efefef;
        padding: 6px 8px;
        // 滚动条样式
        &::-webkit-scrollbar {
          width: 8px;
          height: 8px;
        }
        &::-webkit-scrollbar-corner {
          background-color: transparent;
        }
        &::-webkit-scrollbar-thumb{
          border: 1px solid #f2f2f2;
          background-color: #5b607a;
          border-radius: 4px;
        }
        &::-webkit-scrollbar-track{
          background: 0 0;
          width: 8px;
        }
        .text{
          white-space: pre-line;
          min-height: 100%;
          font-size: 14px;
          line-height: 20px;
          &.no-data{
            font-size: 14px;
            color: #999999;
            position: relative;
            &:after{
              content: '请等待面试官出题';
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
            }
          }
        }
      }
      .question-input{
        width: 100%;
        height: 100%;
        border: none;
        border-radius: 4px;
        padding: 6px 8px;
        background-color: #fffffe;
        font-size: 14px;
        line-height: 20px;
        // 滚动条样式
        &::-webkit-scrollbar {
          width: 8px;
          height: 8px;
        }
        &::-webkit-scrollbar-corner {
          background-color: transparent;
        }
        &::-webkit-scrollbar-thumb{
          border: 1px solid #f2f2f2;
          background-color: #5b607a;
          border-radius: 4px;
        }
        &::-webkit-scrollbar-track{
          background: 0 0;
          width: 8px;
        }
      }
    }
  }
  .page-r{
    height: 100%;
    position: relative;
    padding-bottom: 150px;
  }
  .editor-wrap{
    padding: 39px 10px 49px 0;
    height: 100%;
    position: relative;
  }
  .editor-wrap-inner{
    height: 100%;
    background-color: #efefef;
    overflow: hidden;
  }
  .editor-top-bar, .editor-bottom-bar{
    position: absolute;
    left: 0;
    right: 10px;
    background-color: #efefef;
    overflow: hidden;
    padding: 0 8px;
    overflow: hidden;
  }
  .editor-top-bar{
    height: 29px;
    line-height: 28px;
    top: 10px;
    border-radius: 4px 4px 0 0;
    border-bottom: 1px solid #dcdcdb;
    font-size: 12px;
    select{
      vertical-align: revert;
    }
  }
  .editor-bottom-bar{
    height: 39px;
    line-height: 38px;
    bottom: 10px;
    border-radius: 0 0 4px 4px;
    border-top: 1px solid #dcdcdb;
    .tips{
      font-size: 14px;
      color: #e95e1b;
      float: left;
    }
    .btn{
      height: 28px;
      line-height: 28px;
      border-radius: 4px;
      border: none;
      margin: 5px 0;
      padding: 0 12px;
      font-size: 12px;
      color: #ffffff;
      background: #e74b4b;
      float: right;
    }
  }
  .page-r-b{
    position: absolute;
    bottom: 10px;
    left: 0;
    right: 10px;
    height: 140px;
    border-radius: 4px;
    background-color: #efefef;
    padding: 6px 8px;
    line-height: 20px;
    white-space: pre;
    font-size: 12px;
    &.no-result{
      text-align: center;
      line-height: 128px;
      font-size: 14px;
      color: #999999;
      &:after{
        content: '没有任何内容'
      }
    }
  }
}
</style>
